<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue数组响应性原理详解</title>
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background-color: #f5f7fa;
            color: #333;
            padding: 20px;
            line-height: 1.6;
        }

        .container {
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            color: white;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
        }

        .card {
            background: white;
            border-radius: 10px;
            padding: 25px;
            margin-bottom: 25px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
        }

        .cart-items {
            display: grid;
            gap: 15px;
        }

        .cart-item {
            display: flex;
            align-items: center;
            padding: 15px;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            background: #fafafa;
            transition: all 0.3s ease;
        }

        .cart-item:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .cart-item input[type="checkbox"] {
            margin-right: 15px;
            transform: scale(1.3);
        }

        .item-info {
            flex-grow: 1;
        }

        .item-name {
            font-weight: bold;
            font-size: 1.1rem;
            margin-bottom: 5px;
        }

        .item-price {
            color: #6a11cb;
            font-weight: bold;
        }

        .item-quantity {
            display: flex;
            align-items: center;
            margin: 0 20px;
        }

        .quantity-btn {
            width: 30px;
            height: 30px;
            border: none;
            background: #6a11cb;
            color: white;
            border-radius: 50%;
            cursor: pointer;
            font-weight: bold;
        }

        .quantity-value {
            margin: 0 10px;
            font-weight: bold;
            min-width: 30px;
            text-align: center;
        }

        .remove-btn {
            background: #ff4757;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
            transition: background 0.3s;
        }

        .remove-btn:hover {
            background: #ff2e43;
        }

        .controls {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 20px;
        }

        .select-all {
            display: flex;
            align-items: center;
        }

        .select-all input {
            margin-right: 10px;
            transform: scale(1.2);
        }

        .total {
            font-size: 1.2rem;
            font-weight: bold;
            color: #6a11cb;
        }

        .action-btn {
            background: #6a11cb;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-weight: bold;
            transition: background 0.3s;
        }

        .action-btn:hover {
            background: #2575fc;
        }

        .explanation {
            background: #e3f2fd;
            border-left: 4px solid #2196f3;
            padding: 15px;
            margin: 20px 0;
            border-radius: 0 8px 8px 0;
        }

        .code-block {
            background: #263238;
            color: #eee;
            padding: 15px;
            border-radius: 5px;
            margin: 15px 0;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
        }

        .warning {
            background: #ffecb3;
            border-left: 4px solid #ffc107;
            padding: 15px;
            margin: 20px 0;
            border-radius: 0 8px 8px 0;
        }

        .tabs {
            display: flex;
            border-bottom: 1px solid #ddd;
            margin-bottom: 20px;
        }

        .tab {
            padding: 10px 20px;
            cursor: pointer;
            border-bottom: 3px solid transparent;
        }

        .tab.active {
            border-bottom: 3px solid #6a11cb;
            font-weight: bold;
        }

        .tab-content {
            display: none;
        }

        .tab-content.active {
            display: block;
        }
    </style>
</head>

<body>
    <div id="app" class="container">
        <header>
            <h1>Vue数组响应性原理详解</h1>
            <p>理解为什么有时可以直接修改数组，有时又不能</p>
        </header>

        <div class="card">
            <h2>购物车示例</h2>
            <div class="explanation">
                <p>下面的购物车项目存储在响应式数组中，我们可以直接修改对象属性（如isChecked），因为：</p>
                <ul>
                    <li>数组引用没有改变</li>
                    <li>对象本身是响应式的</li>
                    <li>Vue能够检测到对象属性的变化</li>
                </ul>
            </div>

            <div class="cart-items">
                <div v-for="(item, index) in shopCartList" :key="item.id" class="cart-item">
                    <input type="checkbox" v-model="item.isChecked" @change="updateSessionStorage">
                    <div class="item-info">
                        <div class="item-name">{{ item.name }}</div>
                        <div class="item-price">¥{{ item.price }}</div>
                    </div>
                    <div class="item-quantity">
                        <button class="quantity-btn" @click="decreaseQuantity(index)">-</button>
                        <span class="quantity-value">{{ item.quantity }}</span>
                        <button class="quantity-btn" @click="increaseQuantity(index)">+</button>
                    </div>
                    <button class="remove-btn" @click="removeItem(index)">删除</button>
                </div>
            </div>

            <div class="controls">
                <div class="select-all">
                    <input type="checkbox" v-model="allChecked" @change="toggleAll">
                    <span>全选</span>
                </div>
                <div class="total">
                    总计: ¥{{ totalPrice }}
                </div>
                <button class="action-btn" @click="clearCart">清空购物车</button>
            </div>
        </div>

        <div class="card">
            <h2>响应式原理说明</h2>

            <div class="tabs">
                <div class="tab active" @click="switchTab('working')">可以修改的情况</div>
                <div class="tab" @click="switchTab('notWorking')">不能修改的情况</div>
                <div class="tab" @click="switchTab('solutions')">解决方案</div>
            </div>

            <div id="working" class="tab-content active">
                <div class="explanation">
                    <p><strong>可以直接修改的情况：</strong></p>
                    <ul>
                        <li>修改数组中对象的属性（如item.isChecked）</li>
                        <li>使用Vue重写的数组方法：push(), pop(), shift(), unshift(), splice(), sort(), reverse()</li>
                    </ul>
                </div>

                <div class="code-block">
                    // 这些操作是响应式的
                    this.shopCartList.forEach(item => {
                    item.isChecked = true // 修改对象属性
                    })

                    this.shopCartList.push(newItem) // 使用数组方法
                    this.shopCartList.splice(index, 1) // 使用数组方法
                </div>
            </div>

            <div id="notWorking" class="tab-content">
                <div class="warning">
                    <p><strong>不能直接修改的情况：</strong></p>
                    <ul>
                        <li>直接通过索引设置数组项：this.shopCartList[index] = newItem</li>
                        <li>修改数组长度：this.shopCartList.length = 0</li>
                    </ul>
                </div>

                <div class="code-block">
                    // 这些操作不是响应式的
                    this.shopCartList[0] = newItem // 不会触发更新
                    this.shopCartList.length = 0 // 不会触发更新
                </div>
            </div>

            <div id="solutions" class="tab-content">
                <div class="explanation">
                    <p><strong>解决方案：</strong>对于非响应式操作，使用Vue.set或数组的splice方法</p>
                </div>

                <div class="code-block">
                    // 使用Vue.set（Vue 2）
                    Vue.set(this.shopCartList, index, newItem)

                    // 使用splice方法
                    this.shopCartList.splice(index, 1, newItem)

                    // 在Vue 3中使用 reactive API
                </div>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, computed, reactive } = Vue

        const app = createApp({
            setup() {
                // 模拟响应式数组
                const shopCartList = reactive([
                    { id: 1, name: '商品A', price: 100, quantity: 1, isChecked: true },
                    { id: 2, name: '商品B', price: 200, quantity: 2, isChecked: false },
                    { id: 3, name: '商品C', price: 300, quantity: 1, isChecked: true },
                    { id: 4, name: '商品D', price: 400, quantity: 3, isChecked: false }
                ])

                // 全选状态
                const allChecked = computed({
                    get: () => shopCartList.every(item => item.isChecked),
                    set: (value) => {
                        shopCartList.forEach(item => {
                            item.isChecked = value
                        })
                        updateSessionStorage()
                    }
                })

                // 总价计算
                const totalPrice = computed(() => {
                    return shopCartList.reduce((total, item) => {
                        return item.isChecked ? total + (item.price * item.quantity) : total
                    }, 0)
                })

                // 增加数量
                const increaseQuantity = (index) => {
                    shopCartList[index].quantity++
                    updateSessionStorage()
                }

                // 减少数量
                const decreaseQuantity = (index) => {
                    if (shopCartList[index].quantity > 1) {
                        shopCartList[index].quantity--
                        updateSessionStorage()
                    }
                }

                // 删除项目
                const removeItem = (index) => {
                    shopCartList.splice(index, 1)
                    updateSessionStorage()
                }

                // 全选/取消全选
                const toggleAll = () => {
                    // 这里通过allChecked的setter自动处理
                }

                // 清空购物车
                const clearCart = () => {
                    // 正确的方式 - 使用splice修改数组
                    shopCartList.splice(0, shopCartList.length)
                    updateSessionStorage()
                }

                // 更新sessionStorage（模拟持久化）
                const updateSessionStorage = () => {
                    console.log('更新sessionStorage:', JSON.stringify(shopCartList))
                    // 实际项目中这里会调用sessionStorage.setItem
                }

                // 标签切换
                const switchTab = (tabId) => {
                    // 移除所有active类
                    document.querySelectorAll('.tab').forEach(tab => {
                        tab.classList.remove('active')
                    })
                    document.querySelectorAll('.tab-content').forEach(content => {
                        content.classList.remove('active')
                    })

                    // 添加active类到当前标签和内容
                    document.querySelector(`.tab:nth-child(${tabId === 'working' ? 1 : tabId === 'notWorking' ? 2 : 3})`).classList.add('active')
                    document.getElementById(tabId).classList.add('active')
                }

                return {
                    shopCartList,
                    allChecked,
                    totalPrice,
                    increaseQuantity,
                    decreaseQuantity,
                    removeItem,
                    toggleAll,
                    clearCart,
                    updateSessionStorage,
                    switchTab
                }
            }
        })

        app.mount('#app')
    </script>
</body>

</html>